<?php
namespace Usap\Model\Common;

class UtilsModel {
    /**
     * 加密数据的填充，专门用于 mcrypt 和 openssl 加密对称切换使用，或者和其他平台语言对称使用
     * 即加密数据可以在其他平台或其他语言能相互解密
     * @param $padding_type ssl3 or pkcs or zero
     * @param $data 数据
     * @param $block_size 数据块大小, 128位的块是16,256位的块是32
     */
    static function dataPadding($padding_type, $data, $block_size){
        $mod = strlen($data) % $block_size;
        if($mod==0 && $padding_type!='pkcs7') return $data; // is soigne.

        $pad_num = $block_size - $mod;
        switch($padding_type){
            case 'pkcs5':
            case 'pkcs7':
            case 'pkcs':
                $pad_num = $mod ? $pad_num : $block_size; # 当 pkcs7 时，数据块虽然整齐，但依然要填充一个最大值
                $pad_str = chr($pad_num);
                break;
            case 'ssl':
            case 'ssl3':
                $pad_str = chr($pad_num-1);
                break;
            case 'zero':
                $pad_str = "\0";
                break;
        }
        return $data . str_repeat($pad_str, $pad_num);
    }

	/**
	 * 十进制数转换成62进制
	 *
	 * @param integer $num
	 * @return string
	 */
	static public function dec2sixty($num) {
		$to = 62;
		$dict = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		$ret = '';
		do {
			$ret = $dict[bcmod($num, $to)] . $ret;
			$num = bcdiv($num, $to);
		} while ($num > 0);
		return $ret;
	}

	/**
	 * 62进制数转换成十进制数
	 *
	 * @param string $num
	 * @return string
	 */
	static public function sixty2dec($num) {
		$from = 62;
		$num = strval($num);
		$dict = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		$len = strlen($num);
		$dec = 0;
		for($i = 0; $i < $len; $i++) {
			$pos = strpos($dict, $num[$i]);
			$dec = bcadd(bcmul(bcpow($from, $len - $i - 1), $pos), $dec);
		}
		return $dec;
	}

	/**
	 * 2进制字符串转62进制字符串,  两个字节一转 ffff -> h31，长度可减小 1/4
	 */
	static public function bin2sixty($str){
        $total=strlen($str);
        $out='';
        for($i=0; $i<$total; $i+=2){
            $out .= str_pad(self::dec2sixty(hexdec(bin2hex(substr($str,$i,2)))),3,'0',STR_PAD_LEFT);
        }
        return $out;
	}

	/**
	 * 62进制字符串转2进制字符串，每3个字节一转 h31 -> ffff
	 */
	static public function sixty2bin($str){
        $total=strlen($str);
        $out='';
        for($i=0; $i<$total; $i+=3){
            $out .= hex2bin(str_pad(dechex(self::sixty2dec(ltrim(substr($str,$i,3),'0'))),4,'0',STR_PAD_LEFT));
        }
        return $out;
    }

    /**
     * 加密，默认为Base64输出
     */
    static public function encrypt($password, $value, $raw=false){
		$len = strlen($password);
		if($len < 16) $password = str_pad($password, 16, $password);
		if($len > 16) $password = substr($password,0,16);

        if(function_exists('openssl_encrypt')){
            $value = openssl_encrypt(self::dataPadding('zero', $value, 16), 'AES-128-CBC', $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, '0000000000000000');
        }else{
            $value = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $password, $value, MCRYPT_MODE_CBC, '0000000000000000');
        }
        return $raw ? $value : base64_encode($value);
    
    }

    /**
     * 解密
     */
    static public function decrypt($password, $value, $raw=false){
		$len = strlen($password);
		if($len < 16) $password = str_pad($password, 16, $password);
		if($len > 16) $password = substr($password,0,16);

        $value = $raw ? $value : base64_decode($value);
        if(function_exists('openssl_encrypt')){
            $value = openssl_decrypt($value, 'AES-128-CBC', $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, '0000000000000000');
        }else{
            $value = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $value, MCRYPT_MODE_CBC, '0000000000000000');
			$value = trim($value,"\0");
        }
        return $value;
    }

    /**
     * 短加密，比 encrypt 出来数据短 1/4 
     */
    static public function shortEncrypt($password, $value){
        return self::bin2sixty(self::encrypt($password, $value, true));
    }

    /**
     * 短解密
     */
    static public function shortDecrypt($password, $value){
        return self::decrypt($password, self::sixty2bin($value), true);
    }
}
